﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Microsoft.Devices;
using System.Threading;

namespace AllenalaMente.Games.NewPuzzle
{
    public partial class NewPuzzle : UserControl, GameInterface
    {

        private int score, giuste, sbagliate, righe, colonne, pieceSize, difficiulty, selectedImg = 1;
        private DispatcherTimer updateTimer, startTimer;
        private TimeSpan startTime;
        private UITimer UIgameTimer;
        private UIScore UIgameScore;
        private List<PuzzlePiece> puzzlePiecesList, selectPuzzleList;
        private List<UIElement> animatedElemets;
        private int[] ai, arrayToStoreTheCorrectPieces;
        public UIElement animatedElement;
        Random rnd;

        #region initialize

        public NewPuzzle()
        {
            InitializeComponent();

            puzzlePiecesList = new List<PuzzlePiece>();
            selectPuzzleList = new List<PuzzlePiece>();
            animatedElemets = new List<UIElement>();
            rnd = new Random();

            difficiulty = 1;
            righe = 3;
            colonne = 3;
            pieceSize = 100;

            //inizializePuzzle();

            // Start timer
            startTimer = new DispatcherTimer();
            startTimer.Interval = TimeSpan.FromMilliseconds(600);
            startTimer.Tick += new EventHandler(startTimer_Tick);
            startTimer.Start();
            startTime = TimeSpan.FromSeconds(3);
            TextBlockReady.Text = Locale.Strings.Ready;

            // Update timer
            updateTimer = new DispatcherTimer();
            updateTimer.Interval = TimeSpan.FromMilliseconds(20);
            updateTimer.Tick += new EventHandler(updateTimer_Tick);
            //updateTimer.Start();

            // UI Game timer
            UIgameTimer = new UITimer(60);
            LayoutRoot.Children.Add(UIgameTimer);
            UIgameTimer.timeOut += new EventHandler(game_TimeOut);
            //UIgameTimer.Start();

            // UI Game Score
            UIgameScore = new UIScore();
            LayoutRoot.Children.Add(UIgameScore);
            Canvas.SetLeft(UIgameScore, 750);

        }

        #endregion

        #region methods

        private void inizializePuzzle()
        {
            // Seleziono immagine da scomporre
            string pathPicture = rndImage(selectedImg);
            int n = 0, pos = 0;

            // Pezzi da visualizzare in base alla difficoltà
            int piecesToShow = ((difficiulty - 1) * 2) + 4;
            ai = new int[piecesToShow];
            arrayToStoreTheCorrectPieces = new int[difficiulty];

            for (int i = 0; i < ai.Length; i++)
            {
                ai[i] = -1;
            }

            // inizializzo la lista dei pezzi da cercare e quellli da selezionare
            puzzlePiecesList = CreatePuzzlePieces(righe, colonne, pieceSize, pathPicture);
            selectPuzzleList = CreatePuzzlePieces(righe, colonne, pieceSize, pathPicture);

            //Inizializzo i pezzi del puzzle
            for (int i = 0; i < righe * colonne; i++)
            {
                Canvas.SetLeft(puzzlePiecesList[i], (-puzzlePiecesList[i].translateX) + ((pieceSize) * (i % righe)));
                Canvas.SetTop(puzzlePiecesList[i], (-puzzlePiecesList[i].translateY) + ((pieceSize) * (i / colonne)));
                Canvas.SetZIndex(puzzlePiecesList[i], 1);

                PiecesCanvas.Children.Add(puzzlePiecesList[i]);
            }

            // seleziono gli indici dei pezzi da trovare dal puzzle principale
            for (int i = 0; i < difficiulty; i++)
            {
                while (!insertUniqueNumber(n, ai, i)) ;
                arrayToStoreTheCorrectPieces[i] = ai[i];
            }


            // e gli oscuro sul puzzle principale
            for (int i = 0; i < difficiulty; i++)
            {
                FadeOut(PiecesCanvas.Children[ai[i]], 1000);
            }

            // aggiungo i pezzi sbagliati
            for (int i = difficiulty; i < piecesToShow; i++)
            {
                while (!insertUniqueNumber(n, ai, i)) ;
            }

            List<int> li = ai.ToList();

            li = Randomize(li);

            ai = li.ToArray();

            for (int i = 0; i < piecesToShow; i++)
            {
                pos = i;

                if ((i == 4) && (difficiulty == 3))
                    pos = 8;

                Canvas.SetLeft(selectPuzzleList[ai[i]], (5 * (pos % 3)) + (-selectPuzzleList[ai[i]].translateX) + ((pieceSize) * (pos % 3)));
                Canvas.SetTop(selectPuzzleList[ai[i]], (5 * (pos / 3)) + (-selectPuzzleList[ai[i]].translateY) + ((pieceSize) * (pos / 3)));
                Canvas.SetZIndex(selectPuzzleList[ai[i]], 10);

                SelectPiecesCanvas.Children.Add(selectPuzzleList[ai[i]]);

                FadeIn(SelectPiecesCanvas.Children[i], 1000);

                selectPuzzleList[ai[i]].MouseLeftButtonDown += new MouseButtonEventHandler(NewPuzzle_MouseLeftButtonDown);
            }
        }


        /// <summary>
        /// Mischia gli elementi di una lista
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list">lista di elementi</param>
        /// <returns>lista di elementi mischiati</returns>
        public static List<T> Randomize<T>(List<T> list)
        {
            List<T> randomizedList = new List<T>();
            Random rnd = new Random();
            while (list.Count > 0)
            {
                int index = rnd.Next(0, list.Count); //pick a random item from the master list
                randomizedList.Add(list[index]); //place it at the end of the randomized list
                list.RemoveAt(index);
            }
            return randomizedList;
        }

        public bool insertUniqueNumber(int n, int[] ai, int index)
        {
            n = rnd.Next(0, righe * colonne);

            for (int i = 0; i < ai.Length; i++)
            {
                if (n == ai[i])
                    return false;
            }

            ai[index] = n;
            return true;
        }

        private void resetPuzzle()
        {
            PiecesCanvas.Children.Clear();
            SelectPiecesCanvas.Children.Clear();
            puzzlePiecesList.Clear();
            selectPuzzleList.Clear();

            inizializePuzzle();
        }

        private bool checkVictory()
        {
            for (int i = 0; i < PiecesCanvas.Children.Count; i++)
            {
                if (PiecesCanvas.Children[i].Opacity == 0)
                    return false;
            }

            return true;
        }

        public List<PuzzlePiece> CreatePuzzlePieces(int righe, int colonne, int pieceSize, string pathPicture)
        {
            int piecesNumber = righe * colonne;
            List<PuzzlePiece> ppl = new List<PuzzlePiece>();

            for (int i = 0; i < piecesNumber; i++)
            {
                ppl.Add(new PuzzlePiece(pieceSize, i, pathPicture, righe, colonne));
            }

            return ppl;
        }

        private string rndImage(int rnd)
        {
            string pathPicture;

            switch (rnd)
            {

                case 1:
                    //immagine da risolvere
                    pathPicture = "/AllenalaMente;component/Assets/Images/Puzzle/Puzzle1.jpg";
                    break;
                case 2:
                    //immagine da risolvere
                    pathPicture = "/AllenalaMente;component/Assets/Images/Puzzle/Puzzle2.jpg";
                    break;
                case 3:
                    pathPicture = "/AllenalaMente;component/Assets/Images/Backgrounds/SfondoLogic.jpg";
                    break;
                default:
                    pathPicture = "/AllenalaMente;component/Assets/Images/Backgrounds/SfondoLogic.jpg";
                    break;
            }

            return pathPicture;

        }
        #endregion

        #region animation
        // FADE OUT
        /// <summary>
        /// Animazione che fa scomparire gradualmente un elemento della scena
        /// </summary>
        /// <param name="elem">elemnto su cui applicare animazione</param>
        /// <param name="duration">durata in millisecondi / 1 sec = 1000 </param>
        public void FadeOut(UIElement elem, double duration)
        {
            DoubleAnimation doubleAnimation = new DoubleAnimation();
            doubleAnimation.From = 1;
            doubleAnimation.To = 0;
            doubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(duration));

            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity"));
            Storyboard.SetTarget(doubleAnimation, elem);

            Storyboard sb = new Storyboard();
            sb.Children.Add(doubleAnimation);

            //animatedElemets[((PuzzlePiece)elem).Index] = elem;

            //sb.Completed += new EventHandler(sb_Completed);
            sb.Begin();
        }

        //void sb_Completed(object sender, EventArgs e)
        //{
        //    animatedElement.Visibility = Visibility.Collapsed;
        //    animatedElement.Opacity = 1;
        //}

        // FADE IN
        public void FadeIn(UIElement elem, double duration)
        {
            DoubleAnimation doubleAnimation = new DoubleAnimation();
            doubleAnimation.From = 0;
            doubleAnimation.To = 1;
            doubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(duration));

            Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("Opacity"));
            Storyboard.SetTarget(doubleAnimation, elem);

            Storyboard sb = new Storyboard();
            sb.Children.Add(doubleAnimation);
            sb.Begin();
        }

        #endregion

        #region events

        private void startTimer_Tick(object sender, EventArgs e)
        {
            startTime = startTime.Subtract(TimeSpan.FromSeconds(1));
            if (startTime.Seconds < 2)
                TextBlockReady.Text = Locale.Strings.GO;
            if (startTime.Seconds < 1)
            {
                // The countdown is finished, the game can start
                startTimer.Tick -= startTimer_Tick;
                CanvasCountdown.Visibility = Visibility.Collapsed;
                updateTimer.Start();
                UIgameTimer.Start();

                inizializePuzzle();
            }
        }

        void NewPuzzle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var pIndex = ((PuzzlePiece)(sender)).Index;

            for (int i = 0; i < difficiulty; i++)
            {
                if (arrayToStoreTheCorrectPieces[i] == pIndex)
                {

                    PiecesCanvas.Children[arrayToStoreTheCorrectPieces[i]].Opacity = 1;
                    ((PuzzlePiece)(sender)).Opacity = 0.5f;

                    if (checkVictory())
                    {
                        giuste++;
                        score += (100 + (difficiulty * 10));
                        UIgameScore.SetScore(score);
                        rightResRect.Visibility = Visibility.Visible;
                        Animation.FadeOut(rightResRect, 1000);
                        FlipStart.Begin();

                        for (int y = 0; y < selectPuzzleList.Count; y++)
                        {
                            selectPuzzleList[y].MouseLeftButtonDown -= new MouseButtonEventHandler(NewPuzzle_MouseLeftButtonDown);
                        }
                        
                    }

                    return;
                }
            }

            sbagliate++;
            errorRect.Visibility = Visibility.Visible;
            FlipStart.Begin();
            AllenalaMente.Animation.FadeOut(errorRect, 1000);
        }

        private void answerStart()
        {


        }

        void Flip1Half_Completed(object sender, EventArgs e)
        {
            errorRect.Visibility = Visibility.Collapsed;
            rightResRect.Visibility = Visibility.Collapsed;
            FlipEnd.Begin();
            resetPuzzle();

        }
        #endregion

        #region update
        public event GameEvent GameEnd;
        double time = 3, move = 0;
        private void updateTimer_Tick(object sender, EventArgs e)
        {
            if (difficiulty < 3)
                // Aumenta difficoltà ogni 3 esatte fino a difficoltà 3
                if (giuste > 3 * difficiulty)
                {
                    difficiulty++;
                    selectedImg++;
                }
        }


        private void game_TimeOut(object sender, EventArgs e)
        {
            // Finito il tempo
            VibrateController.Default.Stop();
            updateTimer.Stop();
            GameEventArgs gea = new GameEventArgs();
            gea.reason = GameCloseReason.Timeout;
            gea.score = this.score;
            gea.giuste = this.giuste;
            gea.sbagliate = this.sbagliate;
            if (GameEnd != null)
                GameEnd(this, gea);
        }


        #endregion
    }

}